/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;
using System.Text;

using Gtk;
using Gdk;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Protocol;
using Galaxium.Protocol.Gui;
using Galaxium.Gui;
using Galaxium.Gui.GtkGui;

namespace Galaxium.Gui.GtkGui
{
	public abstract class BasicSessionWidget : AbstractSessionWidget
	{
		protected Galaxium.Gui.GtkGui.ContactTreeView _tree_view = null;
		protected Gtk.Table _profile_table = null;
		protected EventBox _image_event_box = null;
		protected Entry _display_name_entry = null;
		protected Entry _display_message_entry = null;
		protected Entry _search_entry = null;
		protected ImageComboBox<IPresence> _status_combo = null;
		protected ImageView _display_image = null;
		
		public BasicSessionWidget (IControllerWindow<Widget> window, ISession session) : base (window, session)
		{
			// Create and setup the status combo
			_status_combo = new ImageComboBox<IPresence> (new ImageComboTextLookup<IPresence> (PresenceTextLookup), new ImageComboPixbufLookup<IPresence> (PresenceImageLookup));
			if (session.Account.InitialPresence != null)
				_status_combo.Select (session.Account.InitialPresence);
			_status_combo.Changed += StatusChangedEvent;
			
			// Create and setup the account display image
			_display_image = new ImageView ();
			_display_image.SwitchTo (GenerateOwnImage ());
			
			// Create and setup the account display name entry
			_display_name_entry = new Entry ();
			_display_name_entry.Text = Session.Account.DisplayName;
			_display_name_entry.FocusOutEvent += DisplayNameLeaveEvent;
			_display_name_entry.KeyPressEvent += DisplayNameSetEvent;
			
			// Create and setup the account display message entry
			_display_message_entry = new Entry ();
			_display_message_entry.Text = Session.Account.DisplayMessage;
			_display_message_entry.FocusOutEvent += PersonalMessageLeaveEvent;
			_display_message_entry.KeyPressEvent += PersonalMessageSetEvent;
			
			// Create and setup the search entry
			_search_entry = new Entry ();
			_search_entry.Changed += SearchTextChanged;
			
			// Create and setup the contact tree view
			_tree_view = new Galaxium.Gui.GtkGui.ContactTreeView (_session);
			
			SortOrder sortOrder;
			if (!Session.Account.UseDefaultListView)
				sortOrder = Session.Account.SortAscending ? SortOrder.Ascending : SortOrder.Descending;
			else
			{
				if (Configuration.ContactList.Section.GetBool (Configuration.ContactList.SortAscending.Name, Configuration.ContactList.SortAscending.Default))
					sortOrder = SortOrder.Ascending;
				else
					sortOrder = SortOrder.Descending;
			}
			
			_tree_view.SortOrder = sortOrder;
			_tree_view.RowActivated += new RowActivatedHandler(TreeRowActivated);
			_tree_view.ButtonPressEvent += new ButtonPressEventHandler(TreeButtonPressed);
			
			_profile_table = new Gtk.Table (3, 3, false);
			_profile_table.ColumnSpacing = 3;
			_profile_table.RowSpacing = 3;
			
			_image_event_box = new EventBox ();
			_image_event_box.ButtonPressEvent += EventBoxButtonPressEvent;
			_image_event_box.Add (_display_image);
			_image_event_box.BorderWidth = 0;
			
		}
		
		public override void Initialize ()
		{
			base.Initialize ();
			
			Label name_label = new Label ("Name:");
			name_label.Xalign = 1.0f;
			
			Label status_label = new Label ("Status:");
			status_label.Xalign = 1.0f;
			
			Label message_label = new Label ("Message:");
			message_label.Xalign = 1.0f;
			
			_profile_table.Attach (name_label, 0, 1, 0, 1, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_profile_table.Attach (_display_name_entry, 1, 2, 0, 1, AttachOptions.Fill | AttachOptions.Expand | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_profile_table.Attach (status_label, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_profile_table.Attach (_status_combo, 1, 2, 1, 2, AttachOptions.Fill | AttachOptions.Expand | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_profile_table.Attach (message_label, 0, 1, 2, 3, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_profile_table.Attach (_display_message_entry, 1, 3, 2, 3, AttachOptions.Fill | AttachOptions.Expand | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			
			_profile_table.FocusChain = new Widget [] { _display_name_entry, _status_combo, _display_message_entry };
			
			// Create the event box/window that will show the account image
			ScrolledWindow sw = new ScrolledWindow ();
			Viewport vp = new Viewport ();
			vp.ShadowType = ShadowType.In;
			vp.BorderWidth = 1;
			vp.Add (_image_event_box);
			sw.Add (vp);
			sw.HeightRequest = 60;
			sw.WidthRequest = 60;
			sw.HscrollbarPolicy = PolicyType.Never;
			sw.VscrollbarPolicy = PolicyType.Never;
			sw.BorderWidth = 0;
			sw.ShowAll ();
			
			_profile_table.Attach (sw, 2, 3, 0, 2, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			
			ScrolledWindow tree_scroll = new ScrolledWindow ();
			tree_scroll.HscrollbarPolicy = PolicyType.Automatic;
			tree_scroll.VscrollbarPolicy = PolicyType.Automatic;
			tree_scroll.Add (_tree_view);
			tree_scroll.ShadowType = ShadowType.In;
			
			HBox search_box = new HBox ();
			
			search_box.PackEnd (_search_entry, true, true, 0);
			search_box.PackEnd (new Label ("Search:"), false, false, 0);
			
			SetSessionWidget (SessionWidgetPositions.Header, _profile_table, false, true, 0);
			SetSessionWidget (SessionWidgetPositions.Content, tree_scroll, true, true, 0);
			SetSessionWidget (SessionWidgetPositions.Search, search_box, true, true, 2);
			
			// Make sure to load up the account cache and fill the display images for all contacts
			IAccountCache cache = CacheUtility.GetAccountCache(_session.Account);
			
			if (cache != null)
			{
				lock (_session.ContactCollection)
				{
					// We need a local copy of the contact collection because
					// if a contact somehow has a display pic with a creator
					// that is not themselves & isn't on our list already
					// they will be added, changing the collection
					IContact[] contacts = Session.ContactCollection.ToArray ();
					
					foreach (IContact contact in contacts)
					{
						IDisplayImage image = cache.GetDisplayImage (contact);
						
						if (image != null)
							contact.DisplayImage = image;
					}
				}
			}
			
			// Fix up the focus chain
			
			// Make sure everything is shown
			_native_widget.ShowAll ();
		}
		
		public override void SwitchTo ()
		{
			
		}
		
		public override void Update ()
		{
			_tree_view.Rebuild ();
			
			_header_box.Visible = Session.Account.UseDefaultListView ? Configuration.ContactList.Section.GetBool (Configuration.ContactList.ShowProfileDetails.Name, Configuration.ContactList.ShowProfileDetails.Default) : Session.Account.ShowProfileDetails;
			_footer_box.Visible = Session.Account.UseDefaultListView ? Configuration.ContactList.Section.GetBool (Configuration.ContactList.ShowSearchBar.Name, Configuration.ContactList.ShowSearchBar.Default) : Session.Account.ShowSearchBar;
		}
		
		public Gdk.Pixbuf GenerateOwnImage()
		{
			IIconSize iconSize = IconSizes.Huge;
			
			Gdk.Pixbuf pixbuf = null;
			
			if (_session.Account.DisplayImage != null)
				if(_session.Account.DisplayImage.ImageBuffer != null)
					if (_session.Account.DisplayImage.ImageBuffer.Length > 0)
						pixbuf = PixbufUtility.GetScaledPixbuf (new Gdk.Pixbuf(_session.Account.DisplayImage.ImageBuffer), 54, 54);
			
			if (pixbuf == null)
				pixbuf = PixbufUtility.GetScaledPixbuf (IconUtility.GetIcon ("galaxium-displayimage", iconSize), 54, 54);
			
			return pixbuf;
		}
		
		protected abstract void SetDisplayImage ();
		
		protected override void DisplaySettingsChange (object sender, PropertyEventArgs args)
		{
			switch (args.Property)
			{
				case "ShowProfileDetails":
					_header_box.Visible = Session.Account.UseDefaultListView ? Configuration.ContactList.Section.GetBool (Configuration.ContactList.ShowProfileDetails.Name, Configuration.ContactList.ShowProfileDetails.Default) : Session.Account.ShowProfileDetails;
					break;
				case "ShowSearchBar":
					_footer_box.Visible = Session.Account.UseDefaultListView ? Configuration.ContactList.Section.GetBool (Configuration.ContactList.ShowSearchBar.Name, Configuration.ContactList.ShowSearchBar.Default) : Session.Account.ShowProfileDetails;
					break;
			}
		}
		
		[GLib.ConnectBefore ()]
		private void TreeButtonPressed (object sender, ButtonPressEventArgs args)
		{
			TreePath path = null;
			
			if (_tree_view.GetPathAtPos ((int)args.Event.X, (int)args.Event.Y, out path))
			{
				IContact contact = _tree_view.GetModelData (path) as IContact;
				
				if (contact != null)
					SessionUtility.SelectedContact = contact;
			}
		}
		
		public abstract IConversation CreateUsableConversation (IContact contact);
		
		private void TreeRowActivated(object sender, RowActivatedArgs args)
		{
			IContact contact = _tree_view.GetModelData (args.Path) as IContact;
			
			if (contact != null)
			{
				IConversation conversation = _session.Conversations.GetPrivateConversation(contact);
				
				if (conversation == null)
					conversation = CreateUsableConversation (contact);
				
				OnActivateChatWidget (new ChatEventArgs (conversation));
			}
		}
		
		private void DisplayNameLeaveEvent (object sender, FocusOutEventArgs args)
		{
			if (Session.Account.DisplayName.CompareTo(_display_name_entry.Text) != 0)
				Session.Account.DisplayName = _display_name_entry.Text;
		}
		
		[GLib.ConnectBefore ()]
		private void DisplayNameSetEvent (object sender, KeyPressEventArgs args)
		{
			uint state = (uint)args.Event.State;
			const uint Normal = 0;
			
			state &= 1101u;
			
			switch (state)
			{
				case Normal:
					if (args.Event.Key == Gdk.Key.Return || args.Event.Key == Gdk.Key.KP_Enter)
						_tree_view.HasFocus = true;
					break;
			}
		}
		
		[GLib.ConnectBefore ()]
		private void PersonalMessageSetEvent (object sender, KeyPressEventArgs args)
		{
			uint state = (uint)args.Event.State;
			const uint Normal = 0;
			
			state &= 1101u;
			
			switch (state)
			{
				case Normal:
					if (args.Event.Key == Gdk.Key.Return || args.Event.Key == Gdk.Key.KP_Enter)
						_tree_view.HasFocus = true;
					break;
			}
		}
		
		private void PersonalMessageLeaveEvent (object sender, FocusOutEventArgs args)
		{
			if ((string.IsNullOrEmpty(Session.Account.DisplayMessage) && 
				!string.IsNullOrEmpty(_display_message_entry.Text)) ||
				(Session.Account.DisplayMessage.CompareTo(_display_message_entry.Text) != 0))
			{
				Session.Account.DisplayMessage = _display_message_entry.Text;
			}
		}
		
		private void StatusChangedEvent (object sender, EventArgs args)
		{
			Session.Account.Presence = _status_combo.GetSelectedItem ();
		}
		
		protected abstract string PresenceTextLookup (IPresence item);
		protected abstract Gdk.Pixbuf PresenceImageLookup (IPresence item);
		
#pragma warning disable 169
		
		private void EventBoxButtonPressEvent(object sender, ButtonPressEventArgs args)
		{
			if (args.Event.Type == Gdk.EventType.TwoButtonPress)
				SetDisplayImage ();
		}
		
		private void SearchTextChanged (object sender, EventArgs e)
		{
			_tree_view.Filter = _search_entry.Text;
		}
		
#pragma warning restore 169
		
		protected override void DisplayImageChange (object sender, EventArgs args)
		{
			_display_image.FadeTo (GenerateOwnImage ());
		}
		
		protected override void DisplayNameChange (object sender, EventArgs args)
		{
			_display_name_entry.Text = Session.Account.DisplayName;
		}
		
		protected override void DisplayMessageChange (object sender, EventArgs args)
		{
			_display_message_entry.Text = Session.Account.DisplayMessage;
		}
		
		protected override void PresenceChange (object sender, EventArgs args)
		{
			Log.Debug ("Presence Change Abstract");
			
			_status_combo.Select (Session.Account.Presence);
			
			// FIXME: I dont like putting activities here.
			ActivityUtility.EmitActivity (this, new EntityPresenceChangeActivity (Session.Account, null));
		}
		
		protected override void SessionContactChanged (object sender, ContactEventArgs args)
		{
			_tree_view.QueueDraw();
		}
		
		protected override void SessionContactReverse (object sender, ContactEventArgs args)
		{
		
		}
		
		protected override void SessionContactPresenceChanged (object sender, EntityChangeEventArgs<IPresence> args)
		{
			// FIXME: Sounding offline used to be here.
			//if (args.Old == BasePresence.Offline)
				//SoundSetUtility.Play(Sound.ContactSignOn);
			//else if (args.New == BasePresence.Offline)
				//SoundSetUtility.Play(Sound.ContactSignOff);
		}
		
		protected override void SessionTransferInvitationReceived (object sender, FileTransferEventArgs args)
		{
			SoundSetUtility.Play (Sound.FileTransferRequest);
			
			args.FileTransfer.TransferCancel += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
			args.FileTransfer.TransferDecline += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
			args.FileTransfer.TransferFailed += delegate { SoundSetUtility.Play (Sound.FileTransferFailed); };
			args.FileTransfer.TransferFinish += delegate { SoundSetUtility.Play (Sound.FileTransferComplete); };
			args.FileTransfer.TransferStart += delegate { SoundSetUtility.Play (Sound.FileTransferBegan); };
			args.FileTransfer.TransferLocalAbort += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
			args.FileTransfer.TransferRemoteAbort += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
		}
		
		protected override void SessionTransferInvitationSent (object sender, FileTransferEventArgs args)
		{
			args.FileTransfer.TransferCancel += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
			args.FileTransfer.TransferDecline += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
			args.FileTransfer.TransferFailed += delegate { SoundSetUtility.Play (Sound.FileTransferFailed); };
			args.FileTransfer.TransferFinish += delegate { SoundSetUtility.Play (Sound.FileTransferComplete); };
			args.FileTransfer.TransferStart += delegate { SoundSetUtility.Play (Sound.FileTransferBegan); };
			args.FileTransfer.TransferLocalAbort += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
			args.FileTransfer.TransferRemoteAbort += delegate { SoundSetUtility.Play (Sound.FileTransferCancel); };
		}
	}
}
